using System;
using System.Collections.Generic;
using System.Linq;

using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace Azure.Infrastructure.Azure
{
    public class AzureRepository<T> : IRepository<T> where T : TableServiceEntity
    {
        private readonly TableServiceContext _serviceContext;
        private readonly CloudStorageAccount _storageAccount;
        private readonly CloudTableClient _tableClient;

        public AzureRepository(CloudStorageAccount storageAccount)
        {
            _storageAccount = storageAccount;
            _serviceContext = new TableServiceContext(_storageAccount.TableEndpoint.ToString(), _storageAccount.Credentials);
            _tableClient = _storageAccount.CreateCloudTableClient();
        }

        protected virtual void EnsureTable()
        {
            _tableClient.CreateTableIfNotExist(For<T>.Table);
        }

        public void Attach(T existingItem)
        {
            EnsureTable();

            _serviceContext.AttachTo(For<T>.Table, existingItem, "*");
        }

        public void Delete(T itemToDelete)
        {
            EnsureTable();

            _serviceContext.AttachTo(For<T>.Table, itemToDelete, "*");
            _serviceContext.DeleteObject(itemToDelete);
            _serviceContext.SaveChanges();
        }

        public void Insert(T newItem)
        {
            EnsureTable();
            
            _serviceContext.AddObject(For<T>.Table, newItem);
            _serviceContext.SaveChanges();
        }

        public T Last()
        {
            EnsureTable();
            
            var results = from p in Table select p;
            var query = results.AsTableServiceQuery();
            var queryResults = query.Execute();

            return queryResults.LastOrDefault();
        }

        public T Load(string partitionKey, string rowKey)
        {
            EnsureTable();

            var results = from p in Table
                          where p.PartitionKey == partitionKey && p.RowKey == rowKey
                          select p;
            var query = results.AsTableServiceQuery();
            var queryResults = query.Execute();

            return queryResults.FirstOrDefault();
        }

        public IEnumerable<T> Load(string partitionKey)
        {
            EnsureTable();

            var results = from p in Table
                          where p.PartitionKey == partitionKey
                          select p;
            var query = results.AsTableServiceQuery();
            return query.Execute();
        }

        public IEnumerable<T> Query(IQueryable<T> query)
        {
            var azureQuery = query.AsTableServiceQuery();
            return azureQuery.Execute();
        }

        public IQueryable<T> Table
        {
            get
            {
                EnsureTable();

                return _serviceContext.CreateQuery<T>(For<T>.Table);
            }
        }

        public IEnumerable<T> Last(int count)
        {
            EnsureTable();

            var linqQuery = (from p in Table select p).Take(count);
            var azureTable = linqQuery.AsTableServiceQuery();
            var results = azureTable.Execute();
            return results;
        }

        public void Update(T post)
        {
            EnsureTable();

            _serviceContext.UpdateObject(post);
            _serviceContext.SaveChanges();
        }
    }
}